package com.sky.controller.notify;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sky.properties.WeChatProperties;
import com.sky.service.OrdersService;
import com.sky.utils.WxPayCallbackVerifier;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.nio.charset.StandardCharsets;
import java.security.SignatureException;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/notify")
@Slf4j
public class PayNotifyController {

    @Autowired
    private OrdersService orderService;

    @Autowired
    private WeChatProperties weChatProperties;

    @Autowired
    private WxPayCallbackVerifier wxPayCallbackVerifier; // 微信支付签名验证器（需自行实现或使用SDK）

    /**
     * 支付成功回调
     */
    @RequestMapping("/paySuccess")
    public void paySuccessNotify(HttpServletRequest request, HttpServletResponse response) {
        try {
            // 1. 读取回调数据
            String body = readData(request);
            log.info("支付回调原始数据: {}", body);

            // 2. 验证签名
            if (!verifyCallbackSignature(request, body)) {
                log.error("支付回调签名验证失败");
                responseToWeixin(response, "FAIL", "签名验证失败");
                return;
            }

            // 3. 解密数据
            String plainText = decryptData(body);
            log.info("支付回调解密后数据: {}", plainText);

            // 4. 解析并验证关键数据
            JSONObject jsonObject = JSON.parseObject(plainText);
            String outTradeNo = jsonObject.getString("out_trade_no");
            String tradeStatus = jsonObject.getString("trade_state");
            String transactionId = jsonObject.getString("transaction_id");

            if (verifyPayCallbackData(outTradeNo, tradeStatus, transactionId)) {
                log.error("支付回调数据验证失败: outTradeNo={}, tradeStatus={}", outTradeNo, tradeStatus);
                responseToWeixin(response, "FAIL", "数据验证失败");
                return;
            }

            // 5. 处理支付成功业务（已实现幂等性）
            if ("SUCCESS".equals(tradeStatus)) {
                orderService.paySuccess(outTradeNo, transactionId);
            }

            // 6. 响应微信
            responseToWeixin(response, "SUCCESS", "处理成功");

        } catch (Exception e) {
            log.error("支付回调处理异常", e);
            try {
                responseToWeixin(response, "FAIL", "处理异常");
            } catch (Exception ex) {
                log.error("支付回调响应异常", ex);
            }
        }
    }

    /**
     * 退款成功回调
     */
    @RequestMapping("/refund")
    public void refundNotify(HttpServletRequest request, HttpServletResponse response) {
        try {
            // 1. 读取回调数据
            String body = readData(request);
            log.info("退款回调原始数据: {}", body);

            // 2. 验证签名
            if (!verifyCallbackSignature(request, body)) {
                log.error("退款回调签名验证失败");
                responseToWeixin(response, "FAIL", "签名验证失败");
                return;
            }

            // 3. 解密数据
            String plainText = decryptData(body);
            log.info("退款回调解密后数据: {}", plainText);

            // 4. 解析并验证关键数据
            JSONObject jsonObject = JSON.parseObject(plainText);
            String outTradeNo = jsonObject.getString("out_trade_no");
            String outRefundNo = jsonObject.getString("out_refund_no");
            String refundStatus = jsonObject.getString("refund_status");

            if (verifyRefundCallbackData(outTradeNo, outRefundNo, refundStatus)) {
                log.error("退款回调数据验证失败: outTradeNo={}, refundStatus={}", outTradeNo, refundStatus);
                responseToWeixin(response, "FAIL", "数据验证失败");
                return;
            }

            // 5. 处理退款业务（已实现幂等性）
            if ("SUCCESS".equals(refundStatus)) {
                orderService.paySuccess(outTradeNo, outRefundNo);
            }

            // 6. 响应微信
            responseToWeixin(response, "SUCCESS", "处理成功");

        } catch (Exception e) {
            log.error("退款回调处理异常", e);
            try {
                responseToWeixin(response, "FAIL", "处理异常");
            } catch (Exception ex) {
                log.error("退款回调响应异常", ex);
            }
        }
    }

    /**
     * 读取请求数据
     */
    private String readData(HttpServletRequest request) throws Exception {
        BufferedReader reader = request.getReader();
        StringBuilder result = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            if (result.length() > 0) {
                result.append("\n");
            }
            result.append(line);
        }
        return result.toString();
    }

    /**
     * 解密微信回调数据
     */
    private String decryptData(String body) throws Exception {
        JSONObject resultObject = JSON.parseObject(body);
        JSONObject resource = resultObject.getJSONObject("resource");

        if (resource == null) {
            throw new IllegalArgumentException("回调数据格式错误，缺少resource字段");
        }

        String ciphertext = resource.getString("ciphertext");
        String nonce = resource.getString("nonce");
        String associatedData = resource.getString("associated_data");

        AesUtil aesUtil = new AesUtil(weChatProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8));
        return aesUtil.decryptToString(
                associatedData.getBytes(StandardCharsets.UTF_8),
                nonce.getBytes(StandardCharsets.UTF_8),
                ciphertext
        );
    }

    /**
     * 验证回调签名
     */
    private boolean verifyCallbackSignature(HttpServletRequest request, String body) throws SignatureException {
        // 从请求头获取签名相关参数
        String signature = request.getHeader("Wechatpay-Signature");
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String serial = request.getHeader("Wechatpay-Serial");

        // 验证参数完整性
        if (StringUtils.isAnyEmpty(signature, timestamp, nonce, serial)) {
            log.error("签名参数不完整");
            return false;
        }

        // 验证时间戳是否过期（5分钟内有效）
        long currentTime = System.currentTimeMillis() / 1000;
        long callbackTime;
        try {
            callbackTime = Long.parseLong(timestamp);
        } catch (NumberFormatException e) {
            log.error("时间戳格式错误");
            return false;
        }

        if (Math.abs(currentTime - callbackTime) > 300) {
            log.error("时间戳已过期: 当前时间={}, 回调时间={}", currentTime, callbackTime);
            return false;
        }

        // 构造验签字符串
        String message = timestamp + "\n" + nonce + "\n" + body + "\n";

        // 调用验证器验证签名
        return wxPayCallbackVerifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature);
    }

    /**
     * 验证支付回调数据
     */
    private boolean verifyPayCallbackData(String outTradeNo, String tradeStatus, String transactionId) {
        return StringUtils.isEmpty(outTradeNo) ||
                StringUtils.isEmpty(tradeStatus) ||
                StringUtils.isEmpty(transactionId);
    }

    /**
     * 验证退款回调数据
     */
    private boolean verifyRefundCallbackData(String outTradeNo, String outRefundNo, String refundStatus) {
        return StringUtils.isEmpty(outTradeNo) ||
                StringUtils.isEmpty(outRefundNo) ||
                StringUtils.isEmpty(refundStatus);
    }

    /**
     * 响应微信支付系统
     */
    private void responseToWeixin(HttpServletResponse response, String code, String message) throws Exception {
        response.setStatus(HttpStatus.OK.value());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());

        Map<String, String> result = new HashMap<>(2);
        result.put("code", code);
        result.put("message", message);

        response.getWriter().write(JSON.toJSONString(result));
        response.flushBuffer();
    }
}
